use lazy_static::lazy_static;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::RwLock;
use tracing::{instrument, warn};
use tracing_subscriber::prelude::*;
use warp::{hyper::body::Bytes, Filter};
use work::{do_cmd, do_get, Client, LockFile, SvCache};

#[tokio::main]
#[instrument(skip_all, name = "main")]
async fn main() {
    base::trace_warn!();

    work::init_cfg();

    lazy_static! {
        static ref CLIENT: Client = Arc::new(base::client::Client::new());
        static ref READ_LOCK: LockFile = RwLock::new(false);
        static ref CACHE: SvCache = SvCache::builder()
            .max_capacity(9_0000)
            .build();
        static ref REAL: SvCache = SvCache::builder()
            .time_to_idle(Duration::from_secs(2 * 60 * 60)) // 2 hours
            .build();
    }

    if let Err(e) = work::cmd::cache::update_all(&READ_LOCK, CACHE.clone()).await {
        warn!(target: "更新缓冲失败", "cache error: {:?}", e);
    }

    tokio::spawn(async move {
        let _ = work::do_task(CACHE.clone(), REAL.clone(), &CLIENT).await;
    });

    tokio::spawn(async move {
        let _ = work::do_custom(CACHE.clone(), &CLIENT).await;
    });

    let md = "work";
    let body = base::body!();

    let post_two = warp::path(md)
        .and(warp::post())
        .and(warp::path::param())
        .and(warp::path::param())
        .and(body)
        .and_then(move |model: String, cmd: String, data: Bytes| async {
            do_cmd(
                data,
                model,
                cmd,
                &READ_LOCK,
                CACHE.clone(),
                REAL.clone(),
                &CLIENT,
            )
            .await
        });

    let get_two = warp::path(md)
        .and(warp::get())
        .and(warp::path::param())
        .and(warp::path::param())
        .and_then(move |model: String, cmd: String| async {
            do_get(model, cmd, CACHE.clone()).await
        });

    let get_one = warp::get().map(move || format!("\nserve: {}\n\n", md));

    let post = post_two.with(base::cors!());
    let get = get_two.or(get_one);
    let opt = base::opt!();

    let route = get.or(opt).or(post);

    warn!("listening on 127.0.0.1:9964");

    warp::serve(route).run(([127, 0, 0, 1], 9964)).await;
}
